﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using IRepository;
using IServices;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Repository;
using Services;
using SqlSugar;
using YL.Core.Orm.SqlSugar;
using YL.NetCore.DI;
using YL.NetCoreApp.Extensions;
using YL.Utils.Json;
using MediatR;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using System.IdentityModel.Tokens.Jwt;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using YL.Utils.Pub;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Unicode;
using Microsoft.AspNetCore.HttpOverrides;
using Senparc.Weixin.RegisterServices;
using Autofac;
using Microsoft.AspNetCore.Http;

namespace EduCMS
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
            JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddSession();

            services.AddControllers();
            //
            services.AddOptions();
            services.AddXsrf();
            services.AddXss();

            //添加jwt验证：
            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                .AddJwtBearer(options =>
                {
                    options.TokenValidationParameters = new TokenValidationParameters
                    {
                        ValidateIssuer = true, //是否验证Issuer
                        ValidateAudience = true, //是否验证Audience
                        //这里采用动态验证的方式，在重新登录时，刷新token，旧token就强制失效了
                        //其实思路就是每次变更audience，用redis或者其他存储来处理
                        //AudienceValidator = (m, n, z) => 
                        //{
                        //    return m != null && m.FirstOrDefault().Equals(PubConst.ValidAudience);
                        //},
                        ValidateLifetime = true, //是否验证失效时间
                        ClockSkew = TimeSpan.FromSeconds(30),
                        ValidAudience = PubConst.Domain,//Audience
                        ValidIssuer = PubConst.Domain,//Issuser 这两项和前面签发的jwt的设置一致
                        ValidateIssuerSigningKey = true,
                        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(PubConst.SecurityKey))
                    };

                    options.Events = new JwtBearerEvents
                    {
                        OnAuthenticationFailed = context => 
                        {
                            //token expired
                            if (context.Exception.GetType() == typeof(SecurityTokenExpiredException)) 
                            {
                                context.Response.Headers.Add("Token-Expired", "true");
                            }
                            return Task.CompletedTask;
                        }
                    };
                });

            //https://blog.csdn.net/ingener/article/details/104311641
            //json输出时间格式的处理
            services.AddControllersWithViews().AddJsonOptions(options => {
                options.JsonSerializerOptions.Converters.Add(new DateTimeConverter());
                options.JsonSerializerOptions.Converters.Add(new DateTimeNullConverter());
                options.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.All);
            }).SetCompatibilityVersion(CompatibilityVersion.Version_3_0);

            //添加Sugar
            var sqlSugarConfig = SqlSugarConfig.GetConnectionString(Configuration);
            services.AddSqlSugarClient<SqlSugarClient>(config =>
            {
                config.ConnectionString = sqlSugarConfig.Item2;
                config.DbType = sqlSugarConfig.Item1;
                config.IsAutoCloseConnection = true;
                config.InitKeyType = InitKeyType.Attribute;
            });

            services.AddSenparcWeixinServices(Configuration);

            services.AddJson(o => 
            {
                o.JsonType = JsonType.Jil;
            });
            //注入属性
            //services.AddDIProperty();
            services.AddHttpContextAccessor();

            //services.AddBr(); //br 压缩
            services.AddResponseCompression();//添加压缩
            services.AddResponseCaching(); //响应式缓存
            services.AddMemoryCache();
            //ASP.NET CORE 使用 IOC注入：
            //引用IOC扩展 nuget:installpackage MediatR.Extensions.Microsoft.DependencyInjection
            //目的是为了扫描Handler的实现对象并添加到IOC的容器中
            services.AddMediatR(typeof(Startup).GetTypeInfo().Assembly);

            services.AddNlog();

            RegisterBase(services);
            ServiceExtension.RegisterAssembly(services, "Services");
            ServiceExtension.RegisterAssembly(services, "Repository");
            var bulid = services.BuildServiceProvider();
            ServiceResolve.SetServiceResolve(bulid);

        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            //添加jwt验证
            app.UseAuthentication();

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            // app.UseHttpsRedirection();

            app.UseRouting();

            app.UseAuthorization();

            app.UseForwardedHeaders(new ForwardedHeadersOptions 
            {
                ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
            });

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }

        /// <summary>
        /// 泛型注册
        /// </summary>
        /// <param name="services"></param>
        /// <param name="injection"></param>
        private static void RegisterBase(IServiceCollection services, ServiceLifetime injection = ServiceLifetime.Scoped)
        {
            switch (injection)
            {
                case ServiceLifetime.Scoped:
                    services.AddScoped(typeof(IBaseRepository<>), typeof(BaseRepository<>));
                    services.AddScoped(typeof(IBaseServices<>), typeof(BaseServices<>));
                    break;

                case ServiceLifetime.Singleton:
                    services.AddSingleton(typeof(IBaseRepository<>), typeof(BaseRepository<>));
                    services.AddSingleton(typeof(IBaseServices<>), typeof(BaseServices<>));
                    break;

                case ServiceLifetime.Transient:
                    services.AddTransient(typeof(IBaseRepository<>), typeof(BaseRepository<>));
                    services.AddTransient(typeof(IBaseServices<>), typeof(BaseServices<>));
                    break;
            }
        }

        //public void ConfigureContainer(ContainerBuilder builder)
        //{
        //    builder.RegisterGeneric(typeof(BaseRepository<>))
        //        .As(typeof(IBaseRepository<>))
        //        .PropertiesAutowired()
        //        .InstancePerLifetimeScope();

        //    builder.RegisterGeneric(typeof(BaseServices<>))
        //        .As(typeof(IBaseServices<>))
        //        .PropertiesAutowired()
        //        .InstancePerLifetimeScope();

        //    //查找程序集中以Repository结尾的类型 反射注入
        //    builder
        //    .RegisterAssemblyTypes(AppDomain.CurrentDomain.GetAssemblies())
        //    .Where(t => t.Name.EndsWith("Repository"))
        //    .AsImplementedInterfaces();

        //    builder
        //    .RegisterAssemblyTypes(AppDomain.CurrentDomain.GetAssemblies())
        //    .Where(t => t.Name.EndsWith("Services"))
        //    .AsImplementedInterfaces();
        //}
    }
}
